package org.nvm.dic;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.nvm.util.CharacterUtils;

public class DictSegment implements Comparable<DictSegment> {

	private static final int MAX_ARRAY_LENGTH = 5;
	
	private Map<Character, DictSegment> nextCharsMap = null;
	
	private DictSegment[] nextCharsArray;
	
	private Character currentChar;
	
	private int storedCharSize;
	
	private int charNodeState;

	public DictSegment(Character currentChar) {
		super();
		if(currentChar == null) {
			throw new IllegalArgumentException("参数[currentChar]不能为空");
		}
		this.currentChar = currentChar;
	}

	protected Character getCurrentChar() {
		return currentChar;
	}
	
	boolean hasNextNode() {
		return this.storedCharSize > 0;
	}

	Hit matchCharArray(char[] chars, int begin, int length) {
		return matchCharArray(chars, begin, length, null);
	}
	Hit matchCharArray(char[] chars) {
		return matchCharArray(chars, 0, chars.length, null);
	}
	
	Hit matchCharArray(char[] chars, int begin, int length, Hit hit) {
		if(hit == null) {
			hit = new Hit();
			hit.setBegin(begin);
		} else {
			//重置状态
			hit.setUnMatch();
		}
		hit.setEnd(begin);
		
		Character searchChar = chars[begin];
		DictSegment ds = null;
		
		DictSegment[] charArrayTemp = this.nextCharsArray;
		
		if(charArrayTemp != null) {
			DictSegment searchSegment = new DictSegment(searchChar);
			int charIndex = Arrays.binarySearch(charArrayTemp, 0, this.storedCharSize, searchSegment);
			if(charIndex >= 0) {
				ds = charArrayTemp[charIndex];
			}
		} else {
			Map<Character, DictSegment> charMapTemp = this.nextCharsMap;
			ds = charMapTemp.get(searchChar);
		}
		
		if(ds != null) {
			if(length > 1) {
				return ds.matchCharArray(chars, begin + 1, length - 1, hit);
			} else if(length == 1) {
				if(ds.charNodeState == 1 && canBeWord(chars, begin)) {
					hit.setMatch();
				}

				if(ds.hasNextNode()) {
					hit.setPrefixMatch();
					hit.setMatchedDictSegment(ds);
				}
				return hit;
			}
		}
		
		return hit;
	}
	private boolean canBeWord(char[] chars, int begin) {
		int charLength = chars.length;
		int nextChar = begin + 1;
		if(nextChar >= charLength) {
			return true;
		}
		
		int currentType = CharacterUtils.identifyCharType(chars[begin]);
		
		//当前字符是汉字或其他汉字型字符，则直接成词；英文，数字需要进一步判断
		if(currentType == CharacterUtils.CHAR_CHINESE || currentType == CharacterUtils.CHAR_OTHER_CJK) {
			return true;
		}
		
		char ch = chars[nextChar];
		int charType = CharacterUtils.identifyCharType(ch);
		//否则判断后续字符是否是无效字符；后续字符如果是数字或者英文，或是英文允许跟随词（+，-，#），则无法单独成词
		if(charType == CharacterUtils.CHAR_ARABIC ||
				charType == CharacterUtils.CHAR_ENGLISH || charType == CharacterUtils.CHAR_ENGLISH_BASIC_LATIN) {
			return false;
		}

		if(begin > 0) {
			int prevCharType = CharacterUtils.identifyCharType(chars[begin - 1]);
			if(prevCharType == CharacterUtils.CHAR_ARABIC ||
					prevCharType == CharacterUtils.CHAR_ENGLISH || prevCharType == CharacterUtils.CHAR_ENGLISH_BASIC_LATIN) {
				return false;
			}
		}
		
		return true;
	}
	/**
	 * 加载词典
	 * @param chars
	 */
	void fillSegment(char[] chars) {
		fillSegment(chars, 0, chars.length, 1);
	}
	/**
	 * 屏蔽词典
	 * @param chars
	 */
	void disableSegment(char[] chars) {
		fillSegment(chars, 0, chars.length, 0);
	}
	
	
	private synchronized void fillSegment(char[] chars, int begin, int length, int create) {
		DictSegment ds = searchDictSegmentByChar(new Character(chars[begin]), create);
		if(ds != null) {
			if(length > 1) {
				ds.fillSegment(chars, begin + 1, length - 1, create);
			} else {
				ds.charNodeState = create;
			}
		}
	}
	
	private DictSegment searchDictSegmentByChar(Character ch, int create) {
		DictSegment resultDs = null;
		
		if(this.storedCharSize <= MAX_ARRAY_LENGTH) {
			DictSegment[] charArrayTemp = getNextCharArray();
			DictSegment searchSegment = new DictSegment(ch);
			int index = Arrays.binarySearch(charArrayTemp, 0, this.storedCharSize, searchSegment);
			if(index >= 0) {
				resultDs = charArrayTemp[index];
			}
			if(resultDs == null && create == 1) {
				resultDs = searchSegment;
				if(this.storedCharSize < MAX_ARRAY_LENGTH) {
					this.nextCharsArray[this.storedCharSize ++] = resultDs;
					Arrays.sort(this.nextCharsArray, 0, this.storedCharSize);
				} else {
					Map<Character, DictSegment> charMapTemp = getNextCharMap();
					migrateArrayToMap(charArrayTemp, charMapTemp);
					charMapTemp.put(ch, resultDs);
					this.storedCharSize ++;
					this.nextCharsArray = null;
				}
			}
		} else {
			Map<Character, DictSegment> charMapTemp = getNextCharMap();
			resultDs = charMapTemp.get(ch);
			if(resultDs == null && create == 1) {
				resultDs = new DictSegment(ch);
				charMapTemp.put(ch, resultDs);
				this.storedCharSize ++;
			}
		}
		
		return resultDs;
	}

	private void migrateArrayToMap(DictSegment[] arrays, Map<Character, DictSegment> map) {
		for(DictSegment ds : arrays) {
			if(ds != null) {
				map.put(ds.getCurrentChar(), ds);
			}
		}
	}
	
	private DictSegment[] getNextCharArray() {
		if(this.nextCharsArray == null) {
			synchronized (this) {
				if(this.nextCharsArray == null) {
					nextCharsArray = new DictSegment[MAX_ARRAY_LENGTH];
				}
			}
		}
		return this.nextCharsArray;
	}
	
	private Map<Character, DictSegment> getNextCharMap() {
		if(this.nextCharsMap == null) {
			synchronized (this) {
				if(this.nextCharsMap == null) {
					nextCharsMap = new HashMap<>(MAX_ARRAY_LENGTH * 3, 0.8F);
				}
			}
		}
		return this.nextCharsMap;
	}
	
	@Override
	public int compareTo(DictSegment o) {
		
		return this.currentChar.compareTo(o.currentChar);
	}
}
